#include	<stdio.h>
#include	<stdlib.h>
#include	<stdint.h>
#include	<strings.h>
#include	"maze.h"
#include	"player.h"
#include 	"packet.h"
#include 	"object.h"

Team Teams[NUMTEAMS];

/* load
	param: filepath of map
	return: handle to maze matrix 
		-1 on failure to open file
*/
Maze * load( char* filepath ) {
	Maze *m;
    FILE *fh;
    int letter;
    Cell *cells;
  

/** get map dimensions **/
 
   // open map file
   if( ( fh = fopen( filepath, "r")) == NULL) {
		printf("maze.c load(): error opening file\n");
        return (Maze *) -1;
   }
   
	// declare map dimensions
	int rows = 0;
	int cols = 0;

	// while still have chars in file
   while( ( letter = fgetc( fh ) ) != EOF) {
		// find dimensions
		if ( letter == '\n' )  {
			rows++;
		} else if ( rows == 0 )
			cols++;
	}
	// close handle
	fclose(fh);
	
/** allocate memory **/

    m = (Maze *)malloc(sizeof(Maze));
	if (m == NULL) return 0;
	bzero(m, sizeof(Maze));
	
	cells = (Cell *)malloc(rows * cols * sizeof(Cell));
	if (cells == NULL) {
		free(m);
		return 0;
	}
	bzero(cells, rows * cols * sizeof(Cell));
	
// HACK, since files may or may not pick up newlines
	// assume that all maps are square
	if ( rows < cols ){
		cols = rows;
	}
	else if ( cols > rows ){
		rows = cols;
	}	
	//printf("numRows %i numCols %i\n", rows, cols);
	
/** initialize counting variables **/
	// assuming a newline at the end of the file
	int numCells = rows * cols;
	// counting vars
	int home1Count =0;
	int home2Count = 0;
	int jail1Count = 0;
	int jail2Count = 0;
	int cellCount = 0;
	
/**  Read each character and assign to maze structure **/

	// open map file
   if( ( fh = fopen( filepath, "r")) == NULL) {
       return (Maze *) -1;
   }
   
   	// while still have chars in file
	while( ( letter = fgetc( fh ) ) != EOF) {
      // printf("%c",letter);
	   
		// populate map structure, skipping newlines
		if ( letter != '\n' )  {
		
			// assign a position
			cells[cellCount].pos.row = (cellCount / cols)+1;
			cells[cellCount].pos.col = (cellCount % cols)+1;
			//printf("cellCount=%d, row=%d, col=%d\n", cellCount, cells[cellCount].pos.row, cells[cellCount].pos.col);

			// assign team
			if ( (cellCount % cols)  >= cols/2 )
				cells[cellCount].state |= BELONGS_TO_TEAM_2;
				
			switch (letter){
			
				case ' ':
					cells[cellCount].state |= IS_FREE;
					cellCount++;
					break;
					
				case '#':
					cells[cellCount].state |= IS_WALL;
					cellCount++;
					break;
					
				case 'h':
					cells[cellCount].state |= IS_A_HOME;
					// add to list 
					if (home1Count < MAZE_MAX_HOME_CELLS) {
						m->home1[home1Count] = &(cells[cellCount]);
						home1Count++;
					} else {
						fprintf(stderr, "ERROR: mapfile defines more team 1 home cells than"
							"supported (%d)\n", MAZE_MAX_HOME_CELLS);
					}
					cellCount++;
					break;
					
				case 'H':
					cells[cellCount].state |= IS_A_HOME;
					// add to list 
					if (home2Count < MAZE_MAX_HOME_CELLS) {
						m->home2[home2Count] = &(cells[cellCount]);
						home2Count++;
					} else {
						fprintf(stderr, "ERROR: mapfile defines more team 2 home cells than"
							"supported (%d)\n", MAZE_MAX_HOME_CELLS);
					}
					cellCount++;
					break;
					
				case 'j':
					cells[cellCount].state |= IS_A_JAIL;
					// add to list 
					if (jail1Count < MAZE_MAX_JAIL_CELLS) {
						m->jail1[jail1Count] = &(cells[cellCount]);
						jail1Count++;
					} else {
						fprintf(stderr, "ERROR: mapfile defines more team 1 jail cells than"
							"supported (%d)\n", MAZE_MAX_JAIL_CELLS);
					}
					cellCount++;
					break;
					
				case 'J':
					cells[cellCount].state |= IS_A_JAIL;
					// add to list 
					if (jail2Count < MAZE_MAX_JAIL_CELLS) {
						m->jail2[jail2Count] = &(cells[cellCount]);
						jail2Count++;
					} else {
						fprintf(stderr, "ERROR: mapfile defines more team 2 jail cells than"
							"supported (%d)\n", MAZE_MAX_JAIL_CELLS);
					}
					cellCount++;
					break;
					
				default:
					//printf("default [%c]\n", letter);
					break;
			} // end switch
		}  // end if not newline
	} // end file read loop
	
	// close handle
	fclose(fh);
	
/** assign pointers **/

	m->numCells = numCells;
	printf("numcells %i %i %i\n", (m->numCells), rows, cols);
	m->numRows = rows;
	m->numCols = cols;
	m->cells = cells;  
	m->numHome1 = home1Count; 
	m->numJail1 = jail1Count;
	m->numHome2 = home2Count; 
	m->numJail2 = jail2Count;
	
	//printf(" rows %i cols %i cell count = %i \n numhome1 = %i \n numhome2 = %i \n numjail1 = %i \n numjail2 = %i \n", rows, cols, cellCount, home1Count, home2Count, jail1Count, jail2Count);

	return  m;
		
}
	
// returns a pointer to a string of map chars
char * getMapChars( Maze *m, char * filepath ) {
	FILE *fh;
    int letter;

	int numCells = (m->numCells);
	
	char mapStr[numCells+1];
	bzero(&mapStr, sizeof(mapStr));
	
	// open map file
	if( ( fh = fopen( filepath, "r")) == NULL) {
	   return (char *) -1;
	}
	int count = 0;
   	// while still have chars in file
   while( ( letter = fgetc( fh ) ) != EOF) {
		//printf("[%c]", letter);
		if ( (char) letter == ' ' || (char) letter == '#' ||
			(char) letter == 'h' || (char) letter == 'H' ||
			(char) letter == 'j' || (char) letter == 'J') {
			count++;
			char letterChar[2];
			letterChar[0] = (char) letter;
			letterChar[1] = '\0';
			strcat( mapStr, letterChar );
			}
	}
	fclose(fh);
	printf("count %i\n<%s>\n",count, mapStr);
	return mapStr;
}

uint16_t 
maze_dump_cell(Cell *c)
{
	if (c == NULL){
		fprintf(stderr, "NULL\n");
		return 1;
	}
	fprintf(stderr, "c.pos.row=%ld c.pos.col=%ld c.state=0x%lx\n",
		c->pos.row, c->pos.col, c->state);
	return 1;
}

uint16_t
maze_player_add(Maze *m, Player *p)
{

	Cell **home = (p->state & PLAYER_ON_TEAM_1) ? m->home1 : m->home2;
	printf("in maze player %i add is on team 1? %i\n", (p->id), (p->state & PLAYER_ON_TEAM_1));
	uint16_t numHome = (p->state & PLAYER_ON_TEAM_1) ? m->numHome1 : m->numHome2;

	uint16_t playerCnt;

	// check if home cell already has player
	for (playerCnt=0; playerCnt<numHome; playerCnt++){
		if (!(home[playerCnt]->state & HAS_A_PLAYER)){
			break;
		}
	}

	// max number of players = number of home cells
	if (playerCnt<numHome) {
		
		Cell *c = home[playerCnt];
		
		p->cellPtr = c;
		c->state |= HAS_A_PLAYER;
		
		fprintf(stderr, "added player to maze at row=%ld col=%ld: ", c->pos.row, c->pos.col);
		player_dump(p);
	}
	else {
		fprintf(stderr, "ERROR: maze_player_add: can't add player home is full\n");
		return 0;
	}
	return 1;
}


Cell *
maze_find_jail(Maze *m, Player *p)
{
	Cell * c = NULL;
	Cell **jail = (p->state & PLAYER_ON_TEAM_1) ? m->jail2 : m->jail1;
	uint16_t numJail = (p->state & PLAYER_ON_TEAM_1) ? m->numJail2 : m->numJail1;
	uint16_t playerCnt;

	for (playerCnt=0; playerCnt<numJail; playerCnt++){
		if (!(jail[playerCnt]->state & HAS_A_PLAYER)){
			break;
		}
	}
	// max number of players in jail = number of jail cells
	if (playerCnt<numJail) {
		c = jail[playerCnt];
	}
	return c;
}

Position
maze_player_move(Maze *m, Player *p, Position pos)
{
	Position rPos;
	rPos.row = 0;
	rPos.col = 0;
	
	int nRowIdx, nColIdx;
	Cell *ccell, *ncell;

	ccell = p->cellPtr;
	nRowIdx = pos.row - 1;
	nColIdx = pos.col - 1;
	
	if (ccell->pos.row == pos.row && ccell->pos.col == pos.col)
	{
		return rPos;
	}
	if (nRowIdx >=0 && (nRowIdx < m->numRows) && nColIdx>=0 && (nColIdx < m->numCols))
	{
		ncell = getMazeCell(m, nRowIdx, nColIdx);
		uint16_t playerTeamNo = (p->state & PLAYER_ON_TEAM_1) ? PLAYER_ON_TEAM_1 : PLAYER_ON_TEAM_2;
		uint16_t ncellTeamNo = (ncell->state & BELONGS_TO_TEAM_2) ? 2 : 1;
		
		if (CELL_IS_WALL(ncell) && (p->state & PLAYER_HAS_BOMBS))
		{
			CellUpdateBody cBody;
			cBody.pos = ncell->pos;
			cBody.cellType = 0;
			cBody.occupiedByPlayer = 0;
			cBody.occupiedByObject = 0;
	
			CELL_BLAST(ncell);
		}
		
		if (ncell->state & (IS_FREE | IS_A_HOME))
		{
			// player can move if ncell is not occupied or if ncell is occupied by a player from the opposing team
			uint16_t occupiedByTeamNo = CELL_OCCUPIED_BY_TEAM_1(ncell) ? OCCUPIED_BY_TEAM_1 : OCCUPIED_BY_TEAM_2;
			if (!(ncell->state & HAS_A_PLAYER) || ((ncell->state & HAS_A_PLAYER) && (playerTeamNo != occupiedByTeamNo)))
			{	
				CellUpdateBody cBody;
				cBody.cellType = 0;		// CAN ALSO BE HOME CELL: 2
				cBody.occupiedByPlayer = p->id;
				cBody.occupiedByObject = 0;
			
				// a player will get tagged
				if (ncell->state & HAS_A_PLAYER)
				{
					PlayerUpdateBody pBody;
					pBody.field = 0;
					pBody.value = 1;
					
					// this player gets tagged
					if (playerTeamNo != ncellTeamNo)
					{
						PLAYER_TOGGLE_IN_JAIL(p);
						ncell = (Cell *) maze_find_jail(m, p);
						
						printf("\t\t maze_cell_move Player %i is in jail row %i\n", p->id, ncell->pos.row);
						
						cBody.cellType = 3;
						pBody.playerId = p->id;
					}
					// other player gets tagged
					else
					{
						int taggedPlayerId = player_tag(m, ncell, occupiedByTeamNo);
						
						pBody.playerId = taggedPlayerId;
						
						
					}
				}
				// do this also if ncell is not occupied
				// MOVE ACTUALLY HAPPENS
				CELL_TOGGLE_HAS_A_PLAYER(ncell);
				ncell->extra |= playerTeamNo;
				ccell->extra = 0;
				CELL_TOGGLE_HAS_A_PLAYER(ccell);
				p->cellPtr = ncell;
				
				rPos.row = ncell->pos.row;
				rPos.col = ncell->pos.col;
				
				cBody.pos = rPos;
				
				return rPos;
			}
		}
	}
	fprintf(stderr, "ERROR: maze_player_move: can't move player to row=%d col=%d\n", nRowIdx, nColIdx);
	return rPos;
}


uint16_t
maze_object_add(Maze *m, Object *o)
{
	Position pos;
	pos.row = 1;
	pos.col = 1;
	
	Cell * c = getMazeCell(m, pos.row-1, pos.col-1);
	
	while (!CELL_IS_FREE(c)){
		pos.row = rand() % m->numRows + 1;
		pos.col = (o->teamNo == 1) ? rand() % (m->numCols/4) + 1 : m->numCols - (rand() % (m->numCols/4)) + 1;
		
		c = getMazeCell(m, pos.row-1, pos.col-1);
	}
	o->cellPtr = c;
	printf("row=%d, col=%d\n", o->cellPtr->pos.row, o->cellPtr->pos.col);

	if (FLAG_BELONGS_TO_TEAM_1(o)){
		CELL_TOGGLE_HAS_FLAG(c);
		CELL_TOGGLE_HAS_FLAG_1(c);
	}
	else if (FLAG_BELONGS_TO_TEAM_2(o)){
		CELL_TOGGLE_HAS_FLAG(c);
		CELL_TOGGLE_HAS_FLAG_2(c);
	}
	else if (BOMBS_BELONGS_TO_TEAM_1(o)){
		CELL_TOGGLE_HAS_BOMBS(c);
		CELL_TOGGLE_HAS_BOMB_1(c);
	}
	else if (BOMBS_BELONGS_TO_TEAM_2(o)){
		CELL_TOGGLE_HAS_BOMBS(c);
		CELL_TOGGLE_HAS_BOMB_2(c);
	}
	else {
		return 0;
	}
	return 1;
}

